#pragma once

#include <vector>
#include <map>
#include <stack>
#include "Shader.h"
#include "HashedString.h"
#include "Matrix.h"
#include "Material.h"
#include "Shader.h"
#include "Light.h"
#include "VertexContainer.h"
#include "Font.h"
#include "IWindow.h"
#include "Uniform.h"
#include "Texture.h"

#define Renderer	IRenderer::Instance()

class IRenderer
{
public:
	typedef std::map<HashedString, unsigned int> StringLocationMap_t;
	typedef std::pair<HashedString, unsigned int> StringLocationPair_t;
	typedef std::map<HashedString, StringLocationMap_t> ShaderAttributeMap_t;
	typedef std::pair<HashedString, StringLocationPair_t> ShaderAttributePair_t;
public:
	virtual Pointer<IWindow> Initialize() = 0;
	virtual void Deinitialize() = 0;
	// Matrix functions
	void PushProjectionMatrix( Maths::Matrix& matrix );
	void PushModelViewMatrix( Maths::Matrix& matrix );
	void PopProjectionMatrix();
	void PopModelViewMatrix();
	Maths::Matrix& ProjectionMatrix();
	Maths::Matrix& ModelViewMatrix();

	// Renderer Functions
	virtual void SetShader( const HashedString& shader ) = 0;
	virtual void UnsetShader() = 0;
	virtual void SetColor( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) = 0;
	virtual void SetColor( unsigned int color ) = 0;
	virtual void SetMaterial( const Material& material ) = 0;
	virtual void UnsetMaterial() = 0;
	virtual void PushTexture( const HashedString& texture ) = 0;
	virtual void PopTexture() = 0;
	virtual void SetUniforms( const HashedString& shaderName, const UniformData::Map_t& uniforms ) const = 0;
	virtual void SetUniform( const HashedString& shaderName, const HashedString& uniformName, int elements, float* data, bool transpose ) const = 0;
	virtual int GetAttributeLocationByName( const HashedString& program, HashedString& attribute ) = 0;
	virtual void EnableLight( const Light& light ) = 0;
	virtual void DisableLight( const Light& light ) = 0;
	virtual void Render( const IVertexContainer& container ) = 0;
	virtual void SwapBuffer() = 0;
	virtual void ClearBuffer() = 0;

	// Create Functions
	virtual void CreateShaderSource( const HashedString& sourceName, std::string& source, unsigned int shaderType ) = 0;
	virtual void CreateShader( const HashedString& shaderName, std::vector<HashedString> sourceNames ) = 0;
	virtual void FreeShaderSource( const HashedString& sourceName ) = 0;
	virtual void FreeShader( const HashedString& shaderName ) = 0;
	virtual void LinkUniform( const HashedString& shaderName, Uniform& uniform, Uniform::Type type ) = 0;
	virtual void LinkAttribute( const HashedString& shaderName, Attribute& attributeName, Attribute::Type type ) = 0;
	Shader& getShader( HashedString& name );
	virtual void LoadTexture( HashedString& name, Texture& texture, bool freeImage = false ) = 0;
	// Font Functions
	virtual void BuildFont( const HashedString& fontName, unsigned int height, unsigned int width = 0, int angleOfEscape = 0, int orientAngle = 0, unsigned int weight = 0x700, bool italics = false, bool underline = false, bool strikeout = false) = 0;
	virtual void FreeFont( const HashedString& fontName ) = 0;
	virtual void PrintFont( const HashedString& fontName, unsigned int color, int x, int y, const char* fmt, ... ) = 0;
	
	// Renderer Value Functions	
	Pointer<IWindow>& getWindow();
	unsigned int& getShadeModel();
	std::vector<float>& getClearColor();
	float& getClearDepthValue();
	bool& getDepthTest();
	unsigned int& getDepthFunc();
	std::vector<unsigned int>& getHints();
	std::string& getWindowTitle();
	unsigned int& getWindowWidth();
	unsigned int& getWindowHeight();
protected:
	Pointer<IWindow> m_window;
	// Settings
	unsigned int m_shadeModel;
	std::vector<float> m_clearColor;
	float m_clearDepthValue;
	bool m_depthTest;
	unsigned int m_depthFunc;
	std::vector<unsigned int> m_hints;
	std::string m_windowTitle;
	unsigned int m_windowWidth;
	unsigned int m_windowHeight;
protected:
	// Lights
	unsigned int m_lights;
protected:
	// Matrix stacks
	std::stack<Maths::Matrix> m_modelViewStack;
	std::stack<Maths::Matrix> m_projectionStack;
protected:
	// Shader Values
	Shader::Map_t m_shaders;
	std::map<HashedString, unsigned int> m_shaderSources;
	Shader::AttributeMap_t m_shaderAttributes;
	Shader::UniformMap_t m_shaderUniforms;
	unsigned int m_activeShader;
protected:
	// Textures
protected:
	Texture::Map_t m_textures;
	unsigned int m_activeTexture;
	// Font Values
	Font::Map_t m_fonts;
protected:
	static IRenderer* m_Instance;
 	IRenderer(void);
public:
	static IRenderer* Instance();
	virtual ~IRenderer(void);
};